栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

一、首先导入生成二维码和微信支付环境
    
    
      com.google.zxing
      core
      3.2.1
    
    
      com.google.zxing
      javase
      3.2.0
    
    
    
      com.github.wxpay
      wxpay-sdk
      0.0.3
    
二、在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{

  
  private String appID;

  
  private String mchID;

  
  private String key;

  
  private String certPath;

  
  private int httpConnectTimeoutMs = 8000;

  
  private int httpReadTimeoutMs = 10000;

  
  private String payNotifyUrl;

  
  private String refundNotifyUrl;

  
  private final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

 
  @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 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 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.*;


public class WXPayUtils {
  
  public static Map xmlToMap(String strXML) throws Exception {
    try {
      Map data = new HashMap();
      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;
    }

  }

  
  public static String mapToXml(Map 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;
  }

  
  public static String createSign(SortedMap params, String key){
    StringBuilder sb = new StringBuilder();
    Set> es = params.entrySet();
    Iterator> it = es.iterator();
    while(it.hasNext()){
      Map.Entry 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;
  }

  
  public static Boolean isCorrectSign(SortedMap params, String key){
    String sign = createSign(params, key);
    String wxPaySign = params.get("sign").toUpperCase();

    return wxPaySign.equals(sign);
  }
  
  public static SortedMap getSortedMap(Map map){
    SortedMap sortedMap = new TreeMap<>();
    Iterator 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;
  }

  
  public static Logger getLogger() {
    Logger logger = LoggerFactory.getLogger("wxpay java sdk");
    return logger;
  }

  
  public static long getCurrentTimestamp() {
    return System.currentTimeMillis()/1000;
  }

  
  public static long getCurrentTimestampMs() {
    return System.currentTimeMillis();
  }
  
  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;

  
  @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 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 callBackMap = WXPayUtils.xmlToMap(stringBuffer.toString());
      System.out.println(callBackMap.toString());

      SortedMap 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("   ");
     //这里说明告诉微信你已经成功啦,别给老子重复回调我的方法啦,这里有一个坑,
     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 你设置其他返回类型,就会跟你 给微信返的起冲突,就会导致报错

到此这篇关于SpringBoot微信扫码支付的实现示例的文章就介绍到这了,更多相关SpringBoot微信扫码支付内容请搜索考高分网以前的文章或继续浏览下面的相关文章希望大家以后多多支持考高分网!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/129225.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号