SpringBoot微信扫码支付的实现示例
一、首先导入生成二维码和微信支付环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!-- 生成二维码工具 --> < 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.导入
代码如下(示例):
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 服务器域名地址 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类引入配置信息
代码如下(示例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 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层
1 2 3 4 5 6 7 8 9 10 | 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类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 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类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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类
提示:到这里没有报错咱们已经成功一半啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | @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加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @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微信扫码支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
微信公众号搜索 “ 脚本之家 ” ,选择关注
程序猿的那些事、送书等活动等着你
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!
相关文章
Mybatis 中Mapper使用package方式配置报错的解决方案
这篇文章主要介绍了Mybatis 中Mapper使用package方式配置报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-07-07
最新评论