信息发布→ 登录 注册 退出

SpringBoot微信扫码支付的实现示例

发布时间:2026-01-11

点击量:
目录
  • 一、首先导入生成二维码和微信支付环境
  • 二、在application.yml文件配置微信所有需的基本配置
    • 1.导入
    • 2.创建MyWXPayConfig类引入配置信息
  • 三、引入 WxPayServiceImpl 实现类
    • 四、引入WxPayService层
      • 五、引入Util类
        • 六、引入WxPayController类
          • 七、MD5加密
            • 总结

              一、首先导入生成二维码和微信支付环境

                  <!-- 生成二维码工具 -->
                  <dependency>
                    <groupId>com.google.zxing</groupId>
                    <artifactId>core</artifactId>
                    <version>3.2.1</version>
                  </dependency>
                  <dependency>
                    <groupId>com.google.zxing</groupId>
                    <artifactId>javase</artifactId>
                    <version>3.2.0</version>
                  </dependency>
                  <!-- 微信支付所需sdk -->
                  <dependency>
                    <groupId>com.github.wxpay</groupId>
                    <artifactId>wxpay-sdk</artifactId>
                    <version>0.0.3</version>
                  </dependency>

              二、在application.yml文件配置微信所有需的基本配置

              1.导入

              代码如下(示例):

              # 服务器域名地址
              server:
               service-domain: //这里写你的域名地址
              #微信app支付
              pay:
               wxpay:
                app:
                 appID: 微信appid
                 mchID: 商户号
                 key: //这个key实在微信支付公众品台自己定义的key 要求36位
                 certPath: static/cert/wxpay/apiclient_cert.p12 # 从微信商户平台下载的安全证书存放的路径、我放在resources下面,切记一定要看看target目录下的class文件下有没有打包apiclient_cert.p12文件
                 payNotifyUrl: # 微信支付成功的异步通知接口 这里引入你的回调接口。
                 //这里直接写https://域名:端口/接口地址,注意一定是线上的接口,因为微信访问不到你本地的接口
              

              2.创建MyWXPayConfig类引入配置信息

              代码如下(示例):

              package com.example.gasstation.config;
              
              import lombok.Data;
              import org.springframework.boot.context.properties.ConfigurationProperties;
              import org.springframework.stereotype.Component;
              
              import java.io.InputStream;
              
              @Data
              @Component
              @ConfigurationProperties(prefix = "pay.wxpay.app")
              public class MyWXPayConfig implements WXPayConfig{
              
                /**
                 * appID
                 */
                private String appID;
              
                /**
                 * 商户号
                 */
                private String mchID;
              
                /**
                 * API 密钥
                 */
                private String key;
              
                /**
                 * API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
                 */
                private String certPath;
              
                /**
                 * HTTP(S) 连接超时时间,单位毫秒
                 */
                private int httpConnectTimeoutMs = 8000;
              
                /**
                 * HTTP(S) 读数据超时时间,单位毫秒
                 */
                private int httpReadTimeoutMs = 10000;
              
                /**
                 * 微信支付异步通知地址
                 */
                private String payNotifyUrl;
              
                /**
                 * 微信退款异步通知地址
                 */
                private String refundNotifyUrl;
              
                /**
                 * 统一下单url
                 */
                private final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
              
               /** 这里实现了一个service层**/
                @Override
                public InputStream getCertStream() {
                  InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
                  return certStream;
                }
                //在同层级下面新建WXPayConfig service层
                package com.example.gasstation.config;
               import java.io.InputStream;
               public interface WXPayConfig {
                 InputStream getCertStream();//不要问我为啥不另起一行,因为我懒
               }
              }

              三、引入 WxPayServiceImpl 实现类

              package com.example.gasstation.server.impl;
              
              import com.example.gasstation.config.MyWXPayConfig;
              import com.example.gasstation.entity.Result;
              import com.example.gasstation.mapper.PayMapper;
              import com.example.gasstation.model.Money_transfer;
              import com.example.gasstation.model.Pay;
              import com.example.gasstation.server.WxPayService;
              import com.example.gasstation.util.HttpClientUtil;
              import com.example.gasstation.util.WXPayUtils;
              import org.springframework.beans.factory.annotation.Autowired;
              import org.springframework.stereotype.Service;
              
              import java.text.DecimalFormat;
              import java.text.SimpleDateFormat;
              import java.util.Date;
              import java.util.Map;
              import java.util.SortedMap;
              import java.util.TreeMap;
              
              @Service
              public class WxPayServiceImpl implements WxPayService {
              
                @Autowired
                private MyWXPayConfig wxPayAppConfig;
              
                @Autowired
                private PayMapper payMapper;
                @Override
                public String save(String orderNo, double amount, String body,Integer uid) {
                  SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
                  // 1. 生成订单
                  // 订单号,流水号,金额,付款状态,创建时间
                  String product_id = WXPayUtils.generateUUID();
                  Pay pay = new Pay();//这里新建一个实体类 用处存入数据库
                  pay.setTradeNo(product_id);
                  pay.setOutTradeNo(orderNo);
                  pay.setBody(body);
                  pay.setPaystatus(1);
                  pay.setUid(uid);
                  pay.setTotalAmount(amount);
                  pay.setGmtCreate(df.format(new Date()));
                  pay.setTradeStatus("0");
                  pay.setAppId(wxPayAppConfig.getAppID());
                  // 生成预支付订单,保存到数据库
                  payMapper.insert(pay);
                  // 调用统一下单方法,返回 codeUrl 地址
                  String codeUrl = unifiedOrder(product_id,orderNo,amount,body);
              
                  return codeUrl;
                }
              
                private String unifiedOrder(String product_id, String orderNo, double amount, String body){
                  // 生成签名
                  try{
                    SortedMap<String, String> params = new TreeMap<>();
                    params.put("appid",wxPayAppConfig.getAppID());
                    params.put("mch_id",wxPayAppConfig.getMchID());
                    params.put("nonce_str", WXPayUtils.generateUUID());
                    params.put("body",body);           // 商品描述
                    params.put("out_trade_no",orderNo);      // 商户订单号
                    params.put("total_fee",String.valueOf((int)(amount*100)));       // 标价金额(单位为分)
                    params.put("spbill_create_ip", "这里写服务器IP");  // 终端IP
              
                    params.put("notify_url", wxPayAppConfig.getPayNotifyUrl());  // 异步接收微信支付结果通知的回调地址
                    params.put("trade_type","NATIVE");         // 交易类型
                    params.put("product_id",product_id);        // 微信支付要求NATIVE支付,此参数必填
              
              
                    // sign 签名
                    String sign = WXPayUtils.createSign(params, wxPayAppConfig.getKey());
                    params.put("sign",sign);
                    System.out.println(sign);
              
                    // map转xml
                    String payXml = WXPayUtils.mapToXml(params);
                    System.out.println(payXml);
              
                    // 统一下单
                    String s = HttpClientUtil.doPost(wxPayAppConfig.getUNIFIED_ORDER_URL(), payXml, 10000);
                    if(null == s){
                      return null;
                    }
                    Map<String, String> unifiedOrderMap = WXPayUtils.xmlToMap(s);
                    System.out.println(unifiedOrderMap.toString());
                    if(unifiedOrderMap != null){
                      // 前台添加定时器,进行轮询操作,直到支付完毕
                      return unifiedOrderMap.get("code_url");
                    }
                  } catch (Exception e){
                    e.printStackTrace();
                  }
                  return null;
                }
              }

              四、引入WxPayService层

              package com.example.gasstation.server;
              
              import com.example.gasstation.model.Money_transfer;
              
              public interface WxPayService {
              
                String save(String orderNo, double amount, String body,Integer uid);
              
                boolean callBackPayUpdate(String outTradeNo,String totalFee);
              }
              
              

              五、引入Util类

              package com.example.gasstation.util;
              
              import com.github.wxpay.sdk.WXPayUtil;
              import org.slf4j.Logger;
              import org.slf4j.LoggerFactory;
              import org.w3c.dom.Node;
              import org.w3c.dom.NodeList;
              import org.w3c.dom.Document;
              import org.w3c.dom.Element;
              
              import javax.xml.parsers.DocumentBuilder;
              import javax.xml.transform.OutputKeys;
              import javax.xml.transform.Transformer;
              import javax.xml.transform.TransformerFactory;
              import javax.xml.transform.dom.DOMSource;
              import javax.xml.transform.stream.StreamResult;
              import java.io.ByteArrayInputStream;
              import java.io.InputStream;
              import java.io.StringWriter;
              import java.util.*;
              
              /**
               * @Author qjp
               */
              public class WXPayUtils {
                /**
                 * XML格式字符串转换为Map
                 *
                 * @param strXML XML字符串
                 * @return XML数据转换后的Map
                 * @throws Exception
                 */
                public static Map<String, String> xmlToMap(String strXML) throws Exception {
                  try {
                    Map<String, String> data = new HashMap<String, String>();
                    DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
                    InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
                    org.w3c.dom.Document doc = documentBuilder.parse(stream);
                    doc.getDocumentElement().normalize();
                    NodeList nodeList = doc.getDocumentElement().getChildNodes();
                    for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                      Node node = nodeList.item(idx);
                      if (node.getNodeType() == Node.ELEMENT_NODE) {
                        org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                        data.put(element.getNodeName(), element.getTextContent());
                      }
                    }
                    try {
                      stream.close();
                    } catch (Exception ex) {
                      // do nothing
                    }
                    return data;
                  } catch (Exception ex) {
                    WXPayUtils.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
                    throw ex;
                  }
              
                }
              
                /**
                 * 将Map转换为XML格式的字符串
                 *
                 * @param data Map类型数据
                 * @return XML格式的字符串
                 * @throws Exception
                 */
                public static String mapToXml(Map<String, String> data) throws Exception {
                  Document document = WXPayXmlUtil.newDocument();
                  Element root = document.createElement("xml");
                  document.appendChild(root);
                  for (String key: data.keySet()) {
                    String value = data.get(key);
                    if (value == null) {
                      value = "";
                    }
                    value = value.trim();
                    Element filed = document.createElement(key);
                    filed.appendChild(document.createTextNode(value));
                    root.appendChild(filed);
                  }
                  TransformerFactory tf = TransformerFactory.newInstance();
                  Transformer transformer = tf.newTransformer();
                  DOMSource source = new DOMSource(document);
                  transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                  transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                  StringWriter writer = new StringWriter();
                  StreamResult result = new StreamResult(writer);
                  transformer.transform(source, result);
                  String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
                  try {
                    writer.close();
                  }
                  catch (Exception ex) {
                  }
                  return output;
                }
              
                /**
                 * 生成微信支付sign
                 */
                public static String createSign(SortedMap<String, String> params, String key){
                  StringBuilder sb = new StringBuilder();
                  Set<Map.Entry<String, String>> es = params.entrySet();
                  Iterator<Map.Entry<String, String>> it = es.iterator();
                  while(it.hasNext()){
                    Map.Entry<String, String> entry = it.next();
                    String k = entry.getKey();
                    String v = entry.getValue();
                    if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
                      sb.append(k + "=" + v + "&");
                    }
                  }
                  sb.append("key=").append(key);
                  String sign = MD5Util.MD5(sb.toString()).toUpperCase();
              
                  return sign;
                }
              
                /**
                 * 校验签名
                 * @param params
                 * @param key
                 * @return
                 */
                public static Boolean isCorrectSign(SortedMap<String, String> params, String key){
                  String sign = createSign(params, key);
                  String wxPaySign = params.get("sign").toUpperCase();
              
                  return wxPaySign.equals(sign);
                }
                /**
                 * 获取有序map
                 * @param map
                 */
                public static SortedMap<String, String> getSortedMap(Map<String, String> map){
                  SortedMap<String, String> sortedMap = new TreeMap<>();
                  Iterator<String> it = map.keySet().iterator();
                  while(it.hasNext()){
                    String key = it.next();
                    String value = map.get(key);
                    String temp = "";
                    if(null != value){
                      temp = value.trim();
                    }
                    sortedMap.put(key, value);
                  }
                  return sortedMap;
                }
              
                /**
                 * 日志
                 * @return
                 */
                public static Logger getLogger() {
                  Logger logger = LoggerFactory.getLogger("wxpay java sdk");
                  return logger;
                }
              
                /**
                 * 获取当前时间戳,单位秒
                 * @return
                 */
                public static long getCurrentTimestamp() {
                  return System.currentTimeMillis()/1000;
                }
              
                /**
                 * 获取当前时间戳,单位毫秒
                 * @return
                 */
                public static long getCurrentTimestampMs() {
                  return System.currentTimeMillis();
                }
                /**
                 * 生成UUID(用来表示一笔订单)
                 * @return
                 */
                public static String generateUUID(){
                  String uuid = UUID.randomUUID().toString()
                      .replaceAll("-","")
                      .substring(0,32);
                  return uuid;
                }
              }
              
              

              引入WXPayXmlUtil类

              package com.example.gasstation.util;
              import javax.xml.XMLConstants;
              import javax.xml.parsers.DocumentBuilder;
              import javax.xml.parsers.DocumentBuilderFactory;
              import javax.xml.parsers.ParserConfigurationException;
              import org.w3c.dom.Document;
              
              public final class WXPayXmlUtil {
                public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
                  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                  documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
                  documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
                  documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
                  documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
                  documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
                  documentBuilderFactory.setXIncludeAware(false);
                  documentBuilderFactory.setExpandEntityReferences(false);
              
                  return documentBuilderFactory.newDocumentBuilder();
                }
              
                public static Document newDocument() throws ParserConfigurationException {
                  return newDocumentBuilder().newDocument();
                }
              }

              六、引入WxPayController类

              提示:到这里没有报错咱们已经成功一半啦

              @RestController
              @RequestMapping("/wxPay")
              public class WxPayController {
              
                @Autowired
                private WxPayService wxPayService;
              
                @Autowired
                private MyWXPayConfig wxPayConfig;
              
                @Autowired
                private WebMvcConfigurer webMvcConfigurer;
              
                /**
                 * 微信支付 生成二维码
                 *
                 * @param money
                 * @return
                 */
                @GetMapping("/pay")
                public void wxPay(Double money,String body,Integer uid ,HttpServletResponse response){
                  Double amount = money;//金额
                  SimpleDateFormat date = new SimpleDateFormat("yyyyMMddHHmmss");
                  String orderNo = date.format(new Date()) + WXPayUtils.getCurrentTimestampMs();
                  String url_code = wxPayService.save(orderNo, amount, body,uid);
                  System.out.println("url_code:----------"+url_code);
                  if(url_code == null){
                    throw new NullPointerException();
                  }
              
                  try {
                    // 生成二维码配置
                    Map<EncodeHintType, Object> hints = new HashMap<>();
                    // 设置纠错等级
                    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
                    // 编码类型
                    hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
              
                    BitMatrix bitMatrix = new MultiFormatWriter().encode(url_code, BarcodeFormat.QR_CODE, 400, 400, hints);
                    OutputStream outputStream = response.getOutputStream();
              
                    MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream);
              
                  } catch (Exception e){
                    e.printStackTrace();
                  }
                }
              
                /**
                 * 微信支付回调接口
                 */
                @RequestMapping("/callback")
                public void OrderCallBack(HttpServletRequest request, HttpServletResponse response) {
                  InputStream inputStream = null;
                  try {
                    inputStream = request.getInputStream();
                    // BufferedReader是包装设计模式,性能更高
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                    StringBuffer stringBuffer = new StringBuffer();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                      stringBuffer.append(line);
                    }
                    bufferedReader.close();
                    inputStream.close();
                    Map<String, String> callBackMap = WXPayUtils.xmlToMap(stringBuffer.toString());
                    System.out.println(callBackMap.toString());
              
                    SortedMap<String, String> sortedMap = WXPayUtils.getSortedMap(callBackMap);
                    // 校验签名是否正确
                    if (WXPayUtils.isCorrectSign(sortedMap, wxPayConfig.getKey())) {
                      System.out.println("签名校验成功!");
                      // 更新订单状态
                      if ("SUCCESS".equals(sortedMap.get("result_code"))) {
                        String outTradeNo = sortedMap.get("out_trade_no"); // 流水号
                        String totalFee = sortedMap.get("total_fee"); // 交易金额
                        if (wxPayService.callBackPayUpdate(outTradeNo, totalFee)) {  // 通知微信订单处理成功
                          response.setContentType("text/xml");
                          response.setContentType("content-type");
                          response.getWriter().println("<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> </xml>");
                          //这里说明告诉微信你已经成功啦,别给老子重复回调我的方法啦,这里有一个坑,
                          response.setContentType("text/xml");
                          response.getWriter().println("SUCCESS")
                          //本身我就只有这两句话,然后就导致微信一直回调我的方法,废了半天的劲才搞好啦,
                          //原因就是格式不对,给他返回的值他不认识,这里可以看一下微信的支付开发文档,虽然文档写的很垃圾
                        }
                      }
                      // 未成功,就都处理为失败订单
                      response.setContentType("text/html");
                      response.getWriter().println("fail");
                    }
                  } catch (IOException e) {
                    e.printStackTrace();
                  } catch (Exception e) {
                    e.printStackTrace();
                  }
                }
              
              

              七、MD5加密

              @Slf4j
              public class MD5Util {
                
                public static String MD5(String source) {
                  return encodeMd5(source.getBytes());
                }
                private static String encodeMd5(byte[] source) {
                  try {
                    return encodeHex(MessageDigest.getInstance("MD5").digest(source));
                  } catch (NoSuchAlgorithmException e) {
                    throw new IllegalStateException(e.getMessage(), e);
                  }
                }
              
                private static String encodeHex(byte[] bytes) {
                  StringBuffer buffer = new StringBuffer(bytes.length * 2);
                  for (int i = 0; i < bytes.length; i++) {
                    if(((int) bytes[i] & 0xff) < 0x10) {
                      buffer.append("0");
                    }
                    buffer.append(Long.toString((int) bytes[i] & 0xff, 16));
                  }
                  return buffer.toString();
                }
              }
              
              

              总结

              有什么不对的地方欢迎各位码友指出问题,因为我也是第一次做这个微信扫码支付
              回调方法返回类型是void 你设置其他返回类型,就会跟你 给微信返的起冲突,就会导致报错

              在线客服
              服务热线

              服务热线

              4008888355

              微信咨询
              二维码
              返回顶部
              ×二维码

              截屏,微信识别二维码

              打开微信

              微信号已复制,请打开微信添加咨询详情!