微信h5支付_微信h5支付开发 - CSDN
精华内容
参与话题
  • 项目支付接口已对接完成 花了1000块钱买的 绝对值 分享给大家
  • 微信支付H5调用支付详解

    万次阅读 2018-05-11 11:20:16
    微信公众号支付H5调用支付详解最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验...
    微信公众号支付H5调用支付详解

    最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验。


    一、配置公众号微信支付  

       需要我们配置微信公众号支付地址和测试白名单。

      

         比如:支付JS页面的地址为 http://www.xxx.com/shop/pay/

                那此处配置www.xxx.com/shop/pay/


      二、开发流程

         借用微信公众号支付api(地址 http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=7_4),我们需要开发的为红色标记出的。如下:

        

     

    三、向微信服务器端下订单

                 调用统一下单接口,这样就能获取微信支付的prepay_id(http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1)。

         在调用该接口前有几个字段是H5支付必须填写的openid

        3.1 获取openid

             可以通过网页授权形式(http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html)

           在微信中发送如下链接

          

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=要跳转的下订单的url&response_type=code&scope=snsapi_base&state=123#wechat_redirect


       3.2 下订单获取prepay_id

        代码如下,实际上是通过post发送一个xml 文件,获取微信服务器端发送过来的prepay_id。

       

    [java] view plain copy
    1. import java.io.ByteArrayInputStream;  
    2. import java.io.IOException;  
    3. import java.io.InputStream;  
    4. import java.io.UnsupportedEncodingException;  
    5. import java.util.Date;  
    6. import java.util.HashMap;  
    7. import java.util.Iterator;  
    8. import java.util.Map;  
    9. import java.util.Map.Entry;  
    10. import java.util.Random;  
    11.   
    12. import javax.servlet.http.HttpServletRequest;  
    13. import javax.servlet.http.HttpServletResponse;  
    14.   
    15. import org.apache.commons.codec.digest.DigestUtils;  
    16. import org.springframework.stereotype.Controller;  
    17. import org.springframework.web.bind.annotation.RequestMapping;  
    18. import org.xmlpull.v1.XmlPullParser;  
    19. import org.xmlpull.v1.XmlPullParserException;  
    20. import org.xmlpull.v1.XmlPullParserFactory;  
    21.   
    22. import com.fasterxml.jackson.databind.JsonNode;  
    23. import com.gson.oauth.Oauth;  
    24. import com.gson.oauth.Pay;  
    25. import com.gson.util.HttpKit;  
    26. import com.sy.util.DatetimeUtil;  
    27. import com.sy.util.JsonUtil;  
    28.   
    29. @Controller  
    30. @RequestMapping("/pay")  
    31. public class WXPayController {  
    32.   
    33.     @RequestMapping(value = "wxprepay.do")  
    34.     public void jspay(HttpServletRequest request, HttpServletResponse response, String callback) throws Exception {  
    35.         // 获取openid  
    36.         String openId = SessionUtil.getAtt(request, "openId");  
    37.         if (openId == null) {  
    38.             openId = getUserOpenId(request);  
    39.         }  
    40.   
    41.         String appid = "wx16691fcb0523c1a4";  
    42.         String paternerKey = "ININGFENG1234567fdfwfdfd1ss234567";  
    43.           
    44.         String out_trade_no = getTradeNo();  
    45.         Map<String, String> paraMap = new HashMap<String, String>();  
    46.         paraMap.put("appid", appid);  
    47.         paraMap.put("attach""测试");  
    48.         paraMap.put("body""测试购买支付");  
    49.         paraMap.put("mch_id""10283271");  
    50.         paraMap.put("nonce_str", create_nonce_str());  
    51.         paraMap.put("openid", openId);  
    52.         paraMap.put("out_trade_no", out_trade_no);  
    53.         paraMap.put("spbill_create_ip", getAddrIp(request));  
    54.         paraMap.put("total_fee""1");  
    55.         paraMap.put("trade_type""JSAPI");  
    56.         paraMap.put("notify_url""http://www.xxx.co/bank/page/wxnotify");  
    57.         String sign = getSign(paraMap, paternerKey);  
    58.         paraMap.put("sign", sign);  
    59.   
    60.         // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder  
    61.         String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
    62.   
    63.         String xml = ArrayToXml(paraMap);  
    64.   
    65.         String xmlStr = HttpKit.post(url, xml);  
    66.   
    67.         // 预付商品id  
    68.         String prepay_id = "";  
    69.   
    70.         if (xmlStr.indexOf("SUCCESS") != -1) {  
    71.             Map<String, String> map = doXMLParse(xmlStr);  
    72.             prepay_id = (String) map.get("prepay_id");  
    73.         }  
    74.   
    75.         Map<String, String> payMap = new HashMap<String, String>();  
    76.         payMap.put("appId", appid);  
    77.         payMap.put("timeStamp", create_timestamp());  
    78.         payMap.put("nonceStr", create_nonce_str());  
    79.         payMap.put("signType""MD5");  
    80.         payMap.put("package""prepay_id=" + prepay_id);  
    81.         String paySign = getSign(payMap, paternerKey);  
    82.           
    83.         payMap.put("pg", prepay_id);  
    84.         payMap.put("paySign", paySign);  
    85.           
    86.           
    87.         WebUtil.response(response, WebUtil.packJsonp(callback, JsonUtil.warpJsonNodeResponse(JsonUtil.objectToJsonNode(payMap)).toString()));  
    88.     }  
    89.   
    90.     /** 
    91.      * map转成xml 
    92.      *  
    93.      * @param arr 
    94.      * @return 
    95.      */  
    96.     public String ArrayToXml(Map<String, String> arr) {  
    97.         String xml = "<xml>";  
    98.   
    99.         Iterator<Entry<String, String>> iter = arr.entrySet().iterator();  
    100.         while (iter.hasNext()) {  
    101.             Entry<String, String> entry = iter.next();  
    102.             String key = entry.getKey();  
    103.             String val = entry.getValue();  
    104.             xml += "<" + key + ">" + val + "</" + key + ">";  
    105.         }  
    106.   
    107.         xml += "</xml>";  
    108.         return xml;  
    109.     }  
    110.   
    111.     // 获取openId  
    112.     private String getUserOpenId(HttpServletRequest request) throws Exception {  
    113.         String code = request.getParameter("code");  
    114.         if (code == null) {  
    115.             String openId = request.getParameter("openId");  
    116.             return openId;  
    117.         }  
    118.         Oauth o = new Oauth();  
    119.         String token = o.getToken(code);  
    120.         JsonNode node = JsonUtil.StringToJsonNode(token);  
    121.         String openId = node.get("openid").asText();  
    122.         return openId;  
    123.     }  
    124.   
    125.     private String create_nonce_str() {  
    126.             String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";  
    127.             String res = "";  
    128.             for (int i = 0; i < 16; i++) {  
    129.                 Random rd = new Random();  
    130.                 res += chars.charAt(rd.nextInt(chars.length() - 1));  
    131.             }  
    132.             return res;  
    133.     }  
    134.       
    135.     private String getAddrIp(HttpServletRequest request){  
    136.         return request.getRemoteAddr();  
    137.     }  
    138.   
    139.     private String create_timestamp() {  
    140.         return Long.toString(System.currentTimeMillis() / 1000);  
    141.     }  
    142.       
    143.     private String getTradeNo(){  
    144.         String timestamp = DatetimeUtil.formatDate(new Date(), DatetimeUtil.DATETIME_PATTERN);  
    145.         return "HZNO" + timestamp;  
    146.     }  
    147.       
    148.     private String getSign(Map<String, String> params, String paternerKey )  
    149.             throws UnsupportedEncodingException {  
    150.         String string1 = Pay.createSign(params, false);  
    151.         String stringSignTemp = string1 + "&key=" + paternerKey;  
    152.         String signValue = DigestUtils.md5Hex(stringSignTemp).toUpperCase();  
    153.         return  signValue;  
    154.     }  
    155.   
    156.     private Map<String, String> doXMLParse(String xml)  
    157.             throws XmlPullParserException, IOException {  
    158.   
    159.         InputStream inputStream = new ByteArrayInputStream(xml.getBytes());  
    160.   
    161.         Map<String, String> map = null;  
    162.   
    163.         XmlPullParser pullParser = XmlPullParserFactory.newInstance()  
    164.                 .newPullParser();  
    165.   
    166.         pullParser.setInput(inputStream, "UTF-8"); // 为xml设置要解析的xml数据  
    167.   
    168.         int eventType = pullParser.getEventType();  
    169.   
    170.         while (eventType != XmlPullParser.END_DOCUMENT) {  
    171.             switch (eventType) {  
    172.             case XmlPullParser.START_DOCUMENT:  
    173.                 map = new HashMap<String, String>();  
    174.                 break;  
    175.   
    176.             case XmlPullParser.START_TAG:  
    177.                 String key = pullParser.getName();  
    178.                 if (key.equals("xml"))  
    179.                     break;  
    180.   
    181.                 String value = pullParser.nextText();  
    182.                 map.put(key, value);  
    183.   
    184.                 break;  
    185.   
    186.             case XmlPullParser.END_TAG:  
    187.                 break;  
    188.   
    189.             }  
    190.   
    191.             eventType = pullParser.next();  
    192.   
    193.         }  
    194.   
    195.         return map;  
    196.     }  
    197.   
    198. }  

    四、H5支付

          H5支付其实很简单,只需要调用微信内嵌浏览器的js方法就行(http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=7_7)

       

    [plain] view plain copy
    1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
    2. <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
    3. <%  
    4.     String path = request.getContextPath();  
    5.     String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";  
    6. %>  
    7. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
    8. <html>  
    9. <head>  
    10. <meta charset="utf-8" />  
    11. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />  
    12. <meta name="apple-mobile-web-app-capable" content="yes" />  
    13. <meta name="apple-mobile-web-app-status-bar-style" content="black" />  
    14. <meta name="format-detection" content="telephone=no" />  
    15. <title>测试支付</title>  
    16. <link href="../css/css.css?v=1.0" rel="stylesheet" type="text/css">  
    17. </head>  
    18.   
    19. <body>  
    20.     <div class="index_box">  
    21.         <div class="apply_name">微信js支付测试</div>  
    22.           
    23.           
    24.         <div class="branch_con">  
    25.             <ul>  
    26.                 <li><span class="name">测试支付信息</span></li>  
    27.             </ul>  
    28.             <p class="cz_btn"><a href="javascript:pay();" class="btn_1">立即支付</a></p>  
    29.         </div>  
    30.     </div>  
    31.   
    32.     <script type="text/javascript" src="../js/zepto.min.js"></script>  
    33.     <script type="text/javascript" src="../js/common.js"></script>  
    34.     <script type="text/javascript">  
    35.        
    36.     var appId = urlparameter("appId");  
    37.     var timeStamp = urlparameter("timeStamp");  
    38.     var nonceStr = urlparameter("nonceStr");  
    39.     var pg = urlparameter("pg");  
    40.     var signType = urlparameter("signType");  
    41.     var paySign = urlparameter("paySign");  
    42.       
    43.       
    44.       function onBridgeReady(){  
    45.            
    46.            WeixinJSBridge.invoke(  
    47.                'getBrandWCPayRequest', {  
    48.                    "appId" : appId,     //公众号名称,由商户传入       
    49.                    "timeStamp": timeStamp,         //时间戳,自1970年以来的秒数       
    50.                    "nonceStr" : nonceStr, //随机串       
    51.                    "package" : "prepay_id=" + pg,       
    52.                    "signType" : signType,         //微信签名方式:       
    53.                    "paySign" : paySign    //微信签名   
    54.                },  
    55.                  
    56.                function(res){       
    57.                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {  
    58.                          
    59.                        alert("支付成功");  
    60.                    }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。   
    61.                }  
    62.            );   
    63.         }  
    64.         
    65.         
    66.         function pay(){  
    67.               
    68.             if (typeof WeixinJSBridge == "undefined"){  
    69.                if( document.addEventListener ){  
    70.                    document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);  
    71.                }else if (document.attachEvent){  
    72.                    document.attachEvent('WeixinJSBridgeReady', onBridgeReady);   
    73.                    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);  
    74.                }  
    75.             }else{  
    76.                onBridgeReady();  
    77.             }   
    78.               
    79.         }  
    80.     </script>  
    81. </body>  
    82. </html>  

    效果如下

    https://blog.csdn.net/u014351782/article/details/52186932

    展开全文
  • 手把手教你完成微信H5支付

    千次阅读 2019-08-16 13:52:58
    关于微信支付支付,个人觉得相比支付宝复杂的太多了,但是慢慢理解起来还是很简单的 1,首先准备工作,下方是一个时序图,认真看,图看懂了做起来真的很简单 , 2,第二按照上图说明,开始下单时,调用微信下单接口是需要...

    关于微信支付支付,个人觉得相比支付宝复杂的太多了,但是慢慢理解起来还是很简单的

    1,首先准备工作,下方是一个时序图,认真看,图看懂了做起来真的很简单

    ,

    2,第二按照上图说明,开始下单时,调用微信下单接口是需要准备一下几个参数

    APIKEY ---------  支付秘钥(微信商户平台可查,需要自己设置)

    appid -----   商户ID(微信商户平台可查)

    body-------   商品名称

    mch_id -----     支付商户号(微信商户平台可查)

    nonce_str ---------- 随机字符串

    /**
         * 获取随机字符串 Nonce Str
         *
         * @return String 随机字符串
         */
        public static String generateNonceStr() {
            char[] nonceChars = new char[32];
            for (int index = 0 ; index < nonceChars.length ; ++index) {
                nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
            }
            return new String(nonceChars);
        }

    out_trade_no------------   订单号

    spbill_create_ip ------------请求IP

    /**
         * 获取用户实际ip
         * @param request
         * @return
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                    //根据网卡取本机配置的IP  
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割  
            if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15  
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }

    total_fee------------ //,字符串类型,获取金额,单位分

    trade_type -------------------//支付类型,H5就是  "MWEB"

    scene_info  -------------------  //{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://www.chujian.live\",\"wap_name\": \"chongzhi\"}}    这个话里面 type就是Wap   wap_url--指的是你们官网地址 ,wap_name 就是网站名称

    notify_url   ----------------- //此路径是微信服务器调用支付结果带了一大批参数多次请求

     

    以下就把几个重要的工具类整理一下,后面会用到,建议务必下载微信开发文档,我这里只是把自己整理补充后的放上去

    /**
         * 生成签名
         *
         * @param data 待签名数据
         * @param key API密钥
         * @return 签名
         */
        public static String generateSignature(final Map<String, String> data, String key) throws Exception {
            return generateSignature(data, key, SignType.MD5);
        }

    /**
         * 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) {
                WXPayUtil.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 {
            org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
            org.w3c.dom.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();
                org.w3c.dom.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;
        }

     

    /**
         * 向指定URL发送GET方法的请求
         * 
         * @param url
         *            发送请求的URL
         * @param param
         *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
         * @return URL 所代表远程资源的响应结果
         */
        public static String sendGet(String url, String param) {
            String result = "";
            BufferedReader in = null;
            try {
                String urlNameString = url + "?" + param;
                System.out.println(urlNameString);
                URL realUrl = new URL(urlNameString);
                // 打开和URL之间的连接
                URLConnection connection = realUrl.openConnection();
                // 设置通用的请求属性
                connection.setRequestProperty("accept", "*/*");
                connection.setRequestProperty("connection", "Keep-Alive");
                connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                // 建立实际的连接
                connection.connect();
                // 获取所有响应头字段
                Map<String, List<String>> map = connection.getHeaderFields();
                // 遍历所有的响应头字段
                for (String key : map.keySet()) {
                    System.out.println(key + "--->" + map.get(key));
                }
                // 定义 BufferedReader输入流来读取URL的响应
                in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                while ((line = in.readLine()) != null) {
                    result += line;
                }
            } catch (Exception e) {
                System.out.println("发送GET请求出现异常!" + e);
                e.printStackTrace();
            }
            // 使用finally块来关闭输入流
            finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            return result;
        }

        /**
         * 向指定 URL 发送POST方法的请求
         * 
         * @param url
         *            发送请求的 URL
         * @param param
         *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
         * @return 所代表远程资源的响应结果
         */
        public static String sendPost(String url, String param) {
            PrintWriter out = null;
            BufferedReader in = null;
            String result = "";
            try {
                URL realUrl = new URL(url);
                // 打开和URL之间的连接
                URLConnection conn = realUrl.openConnection();
                // 设置通用的请求属性  
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                conn.setRequestProperty("contentType", "text/xml");
                conn.setRequestProperty("Charsert", "utf-8");
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                // 获取URLConnection对象对应的输出流
                out = new PrintWriter(conn.getOutputStream());
                // 发送请求参数
                out.print(param);
                // flush输出流的缓冲
                out.flush();
                // 定义BufferedReader输入流来读取URL的响应
                in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = in.readLine()) != null) {
                    result += line;
                }
            } catch (Exception e) {
                System.out.println("发送 POST 请求出现异常!" + e);
                e.printStackTrace();
            }
            //使用finally块来关闭输出流、输入流
            finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            return result;
        }
    3,接下来废话不多少了,直接上代码了

    /**
         * 用户充值金币----------微信H5支付
         */
        @ResponseBody
        @RequestMapping(value = "weixinPayRechargeGold", method = RequestMethod.GET)
        public String weixinPayWap(HttpServletRequest request, HttpServletResponse response, Integer rechargeNumber, String rechargeId) {
            String mweb_url = "";//跳转链接
            //如果是模拟请求则返回null
            boolean bool = rechargeList.contains(rechargeId);
            if (bool == false) {
                return null;
            }
            try {
                //保存充值记录到数据库
               //此处写自己的业务
                //拼接统一下单地址参数
                Map<String, String> paraMap = new HashMap<String, String>();
                //获取请求ip地址
                String ip = WXPayUtil.getIpAddr(request);
                String bodyName = "MEIDAO" + mdRecharge.getNumber();
                Integer money = mdRecharge.getRmb() * 100;//获取金额,单位分
                paraMap.put("appid", WXPayConstants.APPID); //商户ID
                paraMap.put("body", bodyName); //商品名称
                paraMap.put("mch_id", WXPayConstants.MCHID);
                paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); //String 随机字符串
                paraMap.put("out_trade_no", mdRecharge.getNumber());//订单号
                paraMap.put("spbill_create_ip", ip);//请求IP
                paraMap.put("total_fee", money.toString()); //加钱
                paraMap.put("trade_type", "MWEB"); //类型
                paraMap.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://www.chujian.live\",\"wap_name\": \"yinyinchongzhi\"}}");
                paraMap.put("notify_url", "https://www.chujian.live/faint-service/f/weixin/weixinRechargeGoldResult");// 此路径是微信服务器调用支付结果带了一大批参数多次请求
                String paternerKey = WXPayConstants.APIKEY;
                String sign = WXPayUtil.generateSignature(paraMap, paternerKey);
                paraMap.put("sign", sign);
                String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式
                // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder
                String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                String xmlStr = HttpRequest.sendPost(unifiedorder_url, xml);
                //以下内容是返回前端页面的json数据

                if (xmlStr.indexOf("SUCCESS") != -1) {
                    Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
                    mweb_url = (String) map.get("mweb_url");
                    //支付完返回浏览器跳转的地址,如跳到查看订单页面
                    String redirect_url = "******此处写自己的域名******/faint-service/static/h5/app/successh5.html";
                    String redirect_urlEncode = URLEncoder.encode(redirect_url, "utf-8");//对上面地址urlencode
                    mweb_url = mweb_url + "&redirect_url=" + redirect_urlEncode;//拼接返回地址
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return mweb_url;
        }
     

    /**
         * 用户充值金币----------微信h5返回结果
         */
        @RequestMapping("weixinRechargeGoldResult")
        public void weixinResult(HttpServletRequest request, HttpServletResponse response) {
            BufferedReader reader;
            try {
                reader = request.getReader();
                String line = "";
                StringBuffer inputString = new StringBuffer();
                while ((line = reader.readLine()) != null) {
                    inputString.append(line);
                }
                request.getReader().close();
                Map<String, String> notifyMap = WXPayUtil.xmlToMap(inputString.toString());

                if (notifyMap.get("return_code").equals("SUCCESS")) {
                    if (notifyMap.get("result_code").equals("SUCCESS")) {
                        String orderSn = notifyMap.get("out_trade_no"); //商户订单号 
                        String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
                        BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
                        String openid = notifyMap.get("openid"); //如果有需要可以获取
                      ///此处写自己的业务
                    }
                }
                //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
                response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

     

    如有问题,请指教.有问题也可以给楼主留言

    展开全文
  • 微信H5支付 java详细版

    2020-07-30 23:32:00
    微信H5支付 java详细版,一共两个版本,一个是一个大神的版本使用它封装好的jar包,另一个是servlet版本,两个都可以参考下,只要参数没问题两个都可以运行.
  • 微信H5支付功能开发

    千次阅读 2019-06-28 10:44:03
    本文介绍的是微信H5支付功能开发,也就是在微信之外的H5页面支付。 首先我们需要先看微信的官方文档https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1,这里面几乎介绍了全部流程了。等你了解大概流程...

    本文介绍的是微信H5支付功能开发,也就是在微信之外的H5页面支付。

    首先我们需要先看微信的官方文档https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1,这里面几乎介绍了全部流程了。等你了解大概流程之后,需要在微信公众平台和微信商户平台拿到或者配置一下参数。

    appid:公众平台的appid

    商户号:公众平台的商户号

    商户密钥:key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置

    最后在商户平台后台产品中心-->开发配置 中添加H5支付回调域名,域名必须是通过备案的。(下图借用了某个兄弟的图,我懒得上后台截图了)

    花了不少时间去填这些坑,填完了我们就可以吭哧吭哧开工搬砖了!

    写完支付别忘了还有订单查询功能需要开发,主要为了查询出哪些王八蛋就只是来看看不买东西还占库存。不给钱的我们需要把库存要回来。

    /**

    * 保存订单并发起支付请求

    * @param request

    * @param response

    * @throws IOException 

    */

    @RequestMapping(value = "/save")

    public void save(HttpServletRequest request, HttpServletResponse response) throws IOException{

    //判断用户是否登录

    String mobileNumber = (String)request.getSession().getAttribute("mobileNumber");

    if(StringUtils.isEmpty(mobileNumber)){

    return;

    }

    response.setContentType("text/xml;charset=utf-8");

    PrintWriter out = response.getWriter();

    ResponMsg msg = new ResponMsg();

    msg.setSuccess(true);

    String content = "提交成功";

    try{

    //获取用户选择的票务信息,邮寄信息

    String ticketId = request.getParameter("ticketId");

    String ticketCount = request.getParameter("ticketCount");

    String userName = request.getParameter("userName");

    String userAddress = request.getParameter("userAddress");

    String userMobile = request.getParameter("userMobile");

     

    //参数验证

    if(StringUtils.isEmpty(ticketId) || StringUtils.isEmpty(ticketCount) || StringUtils.isEmpty(userAddress) 

    || StringUtils.isEmpty(userMobile) || StringUtils.isEmpty(userName)){

    content = "请正确且完整填写收货信息!";

    msg.setCode(-2);

    return;

    }

    //判断是否有可售票 有则创建订单预留票并发起支付

    ActTicket actTicket = showService.queryTicketById(Integer.valueOf(ticketId));

    if(actTicket.getTicketStock() - Integer.valueOf(ticketCount) < 0){

    content = "抱歉,已售完!感谢关注!";

    msg.setCode(-1);

    return;

    }

    //创建订单 

    // 自己网站上的订单号

    int randomNum  = (int) (Math.random() * 1999+5000);

    //String out_trade_no = TimeUtils.getSysTime("yyyyMMddHHmmss") + randomNum;

    String orderNO = TimeUtils.getSysTime("yyyyMMddHHmmss") + randomNum;

    ActUserAddress actUserAddress = new ActUserAddress(mobileNumber, userName, userMobile,userAddress, orderNO);

    ActOrder actOrder = new ActOrder();

    actOrder.setOrderNumber(orderNO);

    actOrder.setTicketId(actTicket.getId());

    actOrder.setProductName(actTicket.getTicketLevel());

    actOrder.setProductPrice(actTicket.getTicketPrice());

    actOrder.setProductCount(Integer.valueOf(ticketCount));

    actOrder.setTotalPay(actTicket.getTicketPrice() * Integer.valueOf(ticketCount));

    actOrder.setPayType(1);

    actOrder.setStatement(1);

    actOrder.setUserAccount(mobileNumber);

    actOrder.setActUserAddress(actUserAddress);

    System.out.println(mobileNumber + " 创建订单:" +  actOrder.toString());

    //缓存数据

    request.getSession(true).setAttribute("actOrder", actOrder);

    showService.createOrder(actOrder);

    //发起支付 

    content = "/show/wxPayH5";

    msg.setCode(9);

    }catch(Exception e){

    msg.setCode(0);

    msg.setSuccess(false);

    msg.setMessage("当前人数过多,请稍后再来。");

    e.printStackTrace();

    }finally{

    msg.setMessage(content);

    out.print(JsonUtil.toJson(msg));

    out.flush();

    out.close();

    }

    }

     

     

     

    /**

    * 微信H5支付 

    * 统一下单

    * @param request

    * @param response

    * @param model

    * @throws Exception

    */

    @RequestMapping("/wxPayH5")

    public void wxPayH5(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws Exception {

    //System.out.println("pay ===== start===");

    //System.out.println("pay mobile=== " + request.getSession().getAttribute("mobileNumber").toString());

    ActOrder actOrder = (ActOrder)request.getSession(true).getAttribute("actOrder");

    System.out.println("===wxPayH5===start===actOrder===" + actOrder.toString());

    request.getSession().removeAttribute("actOrder");

    Map<String, Object> result = new HashMap<String, Object>();

    result.put("success", false);

    try {

    // 付款金额,必填

    String total_fee = String.valueOf(actOrder.getTotalPay());  //"0.01"

     

    // 账号信息

    String appid = PayConfig.APP_ID; // appid

    String mch_id = PayConfig.MCH_ID; // 商业号

    String key = PayConfig.API_KEY; // key

     

    String currTime = PayCommonUtil.getCurrTime();

    String strTime = currTime.substring(8, currTime.length());

    String strRandom = PayCommonUtil.buildRandom(4) + "";

    String nonce_str = strTime + strRandom;

    // 价格 注意:价格的单位是分

    String order_price = new BigDecimal(total_fee).multiply(new BigDecimal(100)).toString().split("\\.")[0];

    // 自己网站上的订单号

    String out_trade_no = actOrder.getOrderNumber();

    // 获取发起电脑 ip

    String spbill_create_ip = HttpUtil.getRealIp(request);

     

    // 回调接口

    String notify_url = "http://wx.xxx.com/xx/xx/payNotifyMe";

    // 页面跳转同步通知页面路径

    String trade_type = "MWEB";

     

    // 设置package订单参数

    SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();

    packageParams.put("appid", appid);

    packageParams.put("mch_id", mch_id);

    // 生成签名的时候需要你自己设置随机字符串

    packageParams.put("nonce_str", nonce_str);

    packageParams.put("out_trade_no", out_trade_no);

    packageParams.put("total_fee", order_price);

    packageParams.put("spbill_create_ip", spbill_create_ip);

    packageParams.put("notify_url", notify_url);

    packageParams.put("trade_type", trade_type);

    packageParams.put("body", PayConfig.BODY);

    packageParams.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"http://wx.xxxx.com\",\"wap_name\": \"测试\"}}");

    String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);

    System.out.println("订单号:" + out_trade_no + " 的签名:" + sign);

    packageParams.put("sign", sign);

    String requestXML = PayCommonUtil.getRequestXml(packageParams);

    String resXml = HttpUtil.postData(PayConfig.UFDODER_URL, requestXML);

    Map map = XMLUtil.doXMLParse(resXml);

    //String urlCode = (String) map.get("code_url");

    System.out.println("out_trade_no=" + map.get("out_trade_no"));

    System.out.println("prepay_id=" + map.get("prepay_id"));

    //确认支付过后跳的地址,需要经过urlencode处理

    String urlString = URLEncoder.encode("http://wx.xxx.com/xx/xx/xx/list.html", "GBK");

    String mweb_url = map.get("mweb_url")+"&redirect_url="+urlString;

     

    //更新订单状态

    actOrder.setPayOrderNO(map.get("prepay_id").toString());

    actOrder.setStatement(2);

    actOrder.setNote(map.get("sign").toString());

    showService.updateOrderStatement(actOrder);

    System.out.println("==========mweb_url==========" + mweb_url);

    response.sendRedirect(mweb_url);

    //result.put("sHtmlText", urlCode);

    result.put("success", true);

    } catch (Exception e) {

    e.printStackTrace();

    result.put("errormsg", e.getMessage());

    }

    }

     

     

    /**

    * 执行回调 确认支付后处理事件 例如添加金额到数据库等操作

    * @param request

    * @param response

    * @throws Exception

    */

    @RequestMapping("/payNotifyMe")

    public void weixin_notify(HttpServletRequest request, HttpServletResponse response, ModelMap model)throws Exception {

    System.out.println("进入支付h5回调=====================");

    String xmlMsg = readData(request);

    System.out.println("pay notice---------"+xmlMsg);

    Map params = XMLUtil.doXMLParse(xmlMsg);

    try {

    // 过滤空 设置 TreeMap

    SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();

    Iterator it = params.keySet().iterator();

    while (it.hasNext()) {

    String parameter = (String) it.next();

    String parameterValue = params.get(parameter)+"";

    String v = "";

    if (null != parameterValue) {

    v = parameterValue.trim();

    }

    //System.out.println( parameter + " value==============="+v);

    packageParams.put(parameter, v);

    }

    //订单号

    String orderNO = packageParams.get("out_trade_no").toString().trim();

    ActOrder actOrder = new ActOrder();

    actOrder.setOrderNumber(orderNO);

    actOrder.setPayOrderNO(packageParams.get("sign").toString().trim());

    actOrder.setPayOrderNO(packageParams.get("transaction_id").toString());

    String resXml = "";

    // 处理业务开始

    if ("SUCCESS".equals((String) packageParams.get("result_code"))) {

    // 这里是支付成功 

    model.put("msg", "付款成功");

    // 执行自己的业务逻辑   更新订单状态

    actOrder.setStatement(3);

    // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.

    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

    } else {

    model.put("msg", "付款失败");

    actOrder.setStatement(0);

    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[充值失败]]></return_msg>" + "</xml> ";

    }

     

    //更新订单

    showService.updateOrderStatement(actOrder);

     

    BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());

    out.write(resXml.getBytes());

    out.flush();

    out.close();

    } catch (Exception e) {

    e.printStackTrace();

    }

    }

     

    public static String readData(HttpServletRequest request) {

    BufferedReader br = null;

    try {

    StringBuilder result = new StringBuilder();

    br = request.getReader();

    for (String line; (line=br.readLine())!=null;) {

    if (result.length() > 0) {

    result.append("\n");

    }

    result.append(line);

    }

     

    return result.toString();

    } catch (IOException e) {

    throw new RuntimeException(e);

    }

    finally {

    if (br != null)

    try {br.close();} catch (IOException e) {e.printStackTrace();}

    }

    }

    需要完整的代码可以留言哦!可有偿指导开发完整个流程!

     

    展开全文
  • 首先要说说写这篇文章的初衷:集成支付宝支付运行demo都是可以正常运行的,但是我下载下来微信支付的demo,却发现一大堆报错,而且相关文章几乎没有,可能大家觉得没必要,也许你觉得很简单;但是技术大牛都是从...
  • 微信H5支付

    2019-09-24 16:37:23
    微信H5支付准备工作微信统一下单接口这里用到的一些工具类,一一贴出来常见问题 官方文档 准备工作 appid:公众平台的appid mch_id:公众平台的商户号 在商户平台上图这个位置配置h5支付的域名(需要通过备案的...

    官方文档

    总体概述:

    在项目中,首先是本地下单,接下来点击微信支付,我们希望能拉起微信H5支付页面完成支付。
    首先得调用微信统一下单,得到拉起支付页面的链接,返回给前端跳转,页面拉起支付活动,支付完成回到支付完成页面,异步通知到回调接口。
    具体流程如下:
    在这里插入图片描述

    1、用户在商户侧完成下单,使用微信支付进行支付
    2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB
    3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页
    4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)
    5、如支付成功,商户后台会接收到微信侧的异步通知
    6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)
    7、商户在展示页面,引导用户主动发起支付结果的查询
    8,9、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态
    10、展示最终的订单支付结果给用户

    准备工作

    商户接入微信支付,调用API必须遵循以下规则:
    传输方式 为保证交易安全性,采用HTTPS传输
    提交方式 采用POST方法提交
    数据格式 提交和返回数据都为XML格式,根节点名为xml
    字符编码 统一采用UTF-8字符编码
    签名算法 MD5/HMAC-SHA256
    签名要求 请求和接收数据均需要校验签名,详细方法请参考安全规范-签名算法
    证书要求 调用申请退款、撤销订单、红包接口等需要商户api证书,各api接口文档均有说明。
    判断逻辑 先判断协议字段返回,再判断业务返回,最后判断交易状态

    特别提示:
    必须严格按照API的说明进行一单一支付,一单一红包,一单一付款,在未得到支付系统明确的回复之前不要换单,防止重复支付或者重复付款

    在商户平台上图这个位置配置h5支付的域名(需要通过备案的域名,外网能直接访问)
    申请入口:登录商户平台–>开发配置–>支付配置–>H5支付

    微信统一下单接口

    URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
    是否需要证书:否
    请求参数不列举了,下面直接给代码,注意一下几点

    1. 接口中参数支付金额单位为【分】
    2. 交易类型trade_type

    JSAPI–JSAPI支付(或小程序支付)、NATIVE–Native支付、APP–app支付,MWEB–H5支付,不同trade_type决定了调起支付的方式,请根据支付产品正确上传

    MICROPAY–付款码支付,付款码支付有单独的支付接口,所以接口不需要上传,该字段在对账单中会出现

    统一下单代码
    appid:公众平台的appid
    mch_id:公众平台的商户号

      /**
         * H5付款-统一下单
         * @param bizOrder
         * @return
         */
        public Map<String, String> payH5() {
            Map<String, String> resultMap = null;
            try {
                WXPayConfig config = new MyWXPayConfig();
                WXPay wxPay = new WXPay(config);
    
                Map<String, String> map = new HashMap<>();
                map.put("appid", appid);//公众平台的appid
                map.put("mch_id", mch_id);//公众平台的商户号
                map.put("nonce_str", WXPayUtil.generateNonceStr());
                 map.put("body", "腾讯充值中心-QQ会员充值" );
                map.put("out_trade_no",orderNo());//订单号
                map.put("total_fee", orderFee());//订单金额
                map.put("spbill_create_ip", ip);//当前设备的ip
                map.put("notify_url", h5_notify_url);//异步回调地址
                map.put("trade_type", "MWEB");//H5支付的交易类型为MWEB
                map.put("sign_type", "HMAC-SHA256");
                String xmlStr = WXPayUtil.generateSignedXml(map, key);
    
                resultMap = wxPay.unifiedOrder(WXPayUtil.xmlToMap(xmlStr));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return resultMap;
        }
    
    
    
    /**
         * 微信H5支付
         *
         * @param bizOrder
         * @return
         */
        private CommonResponse getPayH5ByWx(BizOrder bizOrder,String ip) {
            CommonResponse commonResponse = new CommonResponse();
            Map<String, String> resultMap = payH5(bizOrder,ip);
            if (resultMap.get("return_code").equals("SUCCESS") && "SUCCESS".equals(resultMap.get("result_code"))) {
               //mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
                if (StringUtils.isNotEmpty(resultMap.get("mweb_url"))) {
                    System.out.println(resultMap.get("mweb_url"));
                    commonResponse.setSuccess(resultMap.get("mweb_url"));
                } else {
                    commonResponse.setFailed("-2", resultMap.get("err_code_des"));
                }
            } else if (resultMap.get("return_code").equals("SUCCESS") && "ORDERPAID".equals(resultMap.get("err_code"))) {
                commonResponse.setFailed("001", "订单已支付,请勿重复支付");
            } else {
                commonResponse.setFailed("-2", resultMap.get("err_code_des"));
            }
            return commonResponse;
        }
    

    统一下单成功以后我们可以得到 mweb_url,
    这时前端js直接返回即可。

     window.location.href = mweb_url;
    

    这里用到的一些工具类,一一贴出来

    package com.github.wxpay.sdk;
    
    import java.io.InputStream;
    
    public abstract class WXPayConfig {
    
    
    
        /**
         * 获取 App ID
         *
         * @return App ID
         */
        public abstract String getAppID();
    
    
        /**
         * 获取 Mch ID
         *
         * @return Mch ID
         */
        public abstract String getMchID();
    
    
        /**
         * 获取 API 密钥
         *
         * @return API密钥
         */
        public abstract String getKey();
    
    
        /**
         * 获取商户证书内容
         *
         * @return 商户证书内容
         */
        public abstract InputStream getCertStream();
    
        /**
         * HTTP(S) 连接超时时间,单位毫秒
         *
         * @return
         */
        public int getHttpConnectTimeoutMs() {
            return 6*1000;
        }
    
        /**
         * HTTP(S) 读数据超时时间,单位毫秒
         *
         * @return
         */
        public int getHttpReadTimeoutMs() {
            return 8*1000;
        }
    
        /**
         * 获取WXPayDomain, 用于多域名容灾自动切换
         * @return
         */
        public abstract IWXPayDomain getWXPayDomain();
    
        /**
         * 是否自动上报。
         * 若要关闭自动上报,子类中实现该函数返回 false 即可。
         *
         * @return
         */
        public boolean shouldAutoReport() {
            return true;
        }
    
        /**
         * 进行健康上报的线程的数量
         *
         * @return
         */
        public int getReportWorkerNum() {
            return 6;
        }
    
    
        /**
         * 健康上报缓存消息的最大数量。会有线程去独立上报
         * 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
         *
         * @return
         */
        public int getReportQueueMaxSize() {
            return 10000;
        }
    
        /**
         * 批量上报,一次最多上报多个数据
         *
         * @return
         */
        public int getReportBatchSize() {
            return 10;
        }
    
    }
    
    
    package com.github.wxpay.sdk;
    
    import com.github.wxpay.sdk.WXPayConstants.SignType;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class WXPay {
    
        private WXPayConfig config;
        private SignType signType;
        private boolean autoReport;
        private boolean useSandbox;
        private String notifyUrl;
        private WXPayRequest wxPayRequest;
    
        public WXPay(final WXPayConfig config) throws Exception {
            this(config, null, true, false);
        }
    
        public WXPay(final WXPayConfig config, final boolean autoReport) throws Exception {
            this(config, null, autoReport, false);
        }
    
    
        public WXPay(final WXPayConfig config, final boolean autoReport, final boolean useSandbox) throws Exception{
            this(config, null, autoReport, useSandbox);
        }
    
        public WXPay(final WXPayConfig config, final String notifyUrl) throws Exception {
            this(config, notifyUrl, true, false);
        }
    
        public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport) throws Exception {
            this(config, notifyUrl, autoReport, false);
        }
    
        public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
            this.config = config;
            this.notifyUrl = notifyUrl;
            this.autoReport = autoReport;
            this.useSandbox = useSandbox;
            if (useSandbox) {
                this.signType = SignType.MD5; // 沙箱环境
            }
            else {
                this.signType = SignType.HMACSHA256;
            }
            this.wxPayRequest = new WXPayRequest(config);
        }
    
        private void checkWXPayConfig() throws Exception {
            if (this.config == null) {
                throw new Exception("config is null");
            }
            if (this.config.getAppID() == null || this.config.getAppID().trim().length() == 0) {
                throw new Exception("appid in config is empty");
            }
            if (this.config.getMchID() == null || this.config.getMchID().trim().length() == 0) {
                throw new Exception("appid in config is empty");
            }
            if (this.config.getCertStream() == null) {
                throw new Exception("cert stream in config is empty");
            }
            if (this.config.getWXPayDomain() == null){
                throw new Exception("config.getWXPayDomain() is null");
            }
    
            if (this.config.getHttpConnectTimeoutMs() < 10) {
                throw new Exception("http connect timeout is too small");
            }
            if (this.config.getHttpReadTimeoutMs() < 10) {
                throw new Exception("http read timeout is too small");
            }
    
        }
    
        /**
         * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
         * 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口
         *
         * @param reqData
         * @return
         * @throws Exception
         */
        public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
            reqData.put("appid", config.getAppID());
            reqData.put("mch_id", config.getMchID());
            reqData.put("nonce_str", WXPayUtil.generateNonceStr());
            if (SignType.MD5.equals(this.signType)) {
                reqData.put("sign_type", WXPayConstants.MD5);
            }
            else if (SignType.HMACSHA256.equals(this.signType)) {
                reqData.put("sign_type", WXPayConstants.HMACSHA256);
            }
            reqData.put("sign", WXPayUtil.generateSignature(reqData, config.getKey(), this.signType));
            return reqData;
        }
    
        /**
         * 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
         *
         * @param reqData 向wxpay post的请求数据
         * @return 签名是否有效
         * @throws Exception
         */
        public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
            // 返回数据的签名方式和请求中给定的签名方式是一致的
            return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
        }
    
        /**
         * 判断支付结果通知中的sign是否有效
         *
         * @param reqData 向wxpay post的请求数据
         * @return 签名是否有效
         * @throws Exception
         */
        public boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception {
            String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE);
            SignType signType;
            if (signTypeInData == null) {
                signType = SignType.HMACSHA256;
            }
            else {
                signTypeInData = signTypeInData.trim();
                if (signTypeInData.length() == 0) {
                    signType = SignType.HMACSHA256;
                }
                else if (WXPayConstants.MD5.equals(signTypeInData)) {
                    signType = SignType.MD5;
                }
                else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) {
                    signType = SignType.HMACSHA256;
                }
                else {
                    throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData));
                }
            }
            return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), signType);
        }
    
    
        /**
         * 不需要证书的请求
         * @param urlSuffix String
         * @param reqData 向wxpay post的请求数据
         * @param connectTimeoutMs 超时时间,单位是毫秒
         * @param readTimeoutMs 超时时间,单位是毫秒
         * @return API返回数据
         * @throws Exception
         */
        public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
                                         int connectTimeoutMs, int readTimeoutMs) throws Exception {
            String msgUUID = reqData.get("nonce_str");
            String reqBody = WXPayUtil.mapToXml(reqData);
    
            String resp = this.wxPayRequest.requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);
            return resp;
        }
    
    
        /**
         * 需要证书的请求
         * @param urlSuffix String
         * @param reqData 向wxpay post的请求数据  Map
         * @param connectTimeoutMs 超时时间,单位是毫秒
         * @param readTimeoutMs 超时时间,单位是毫秒
         * @return API返回数据
         * @throws Exception
         */
        public String requestWithCert(String urlSuffix, Map<String, String> reqData,
                                      int connectTimeoutMs, int readTimeoutMs) throws Exception {
            String msgUUID= reqData.get("nonce_str");
            String reqBody = WXPayUtil.mapToXml(reqData);
    
            String resp = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, this.autoReport);
            return resp;
        }
    
        /**
         * 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
         * @param xmlStr API返回的XML格式数据
         * @return Map类型数据
         * @throws Exception
         */
        public Map<String, String> processResponseXml(String xmlStr) throws Exception {
            String RETURN_CODE = "return_code";
            String return_code;
            Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
            if (respData.containsKey(RETURN_CODE)) {
                return_code = respData.get(RETURN_CODE);
            }
            else {
                throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
            }
    
            if (return_code.equals(WXPayConstants.FAIL)) {
                return respData;
            }
            else if (return_code.equals(WXPayConstants.SUCCESS)) {
               if (this.isResponseSignatureValid(respData)) {
                   return respData;
               }
               else {
                   throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
               }
            }
            else {
                throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
            }
        }
    
         /**
         * 作用:统一下单<br>
         * 场景:公共号支付、扫码支付、APP支付
         * @param reqData 向wxpay post的请求数据
         * @return API返回数据
         * @throws Exception
         */
        public Map<String, String> unifiedOrder(Map<String, String> reqData) throws Exception {
            return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
        }
    
     
     
    
    
       // end class
    
    
    package com.github.wxpay.sdk;
    
    import com.github.wxpay.sdk.WXPayConstants.SignType;
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.servlet.http.HttpServletRequest;
    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.security.MessageDigest;
    import java.security.SecureRandom;
    import java.util.*;
    
    
    public class WXPayUtil {
    
        private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
        private static final Random RANDOM = new SecureRandom();
    
        /**
         * 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) {
                WXPayUtil.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 {
            org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
            org.w3c.dom.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();
                org.w3c.dom.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 的 XML 格式字符串
         *
         * @param data Map类型数据
         * @param key API密钥
         * @return 含有sign字段的XML
         */
        public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
            return generateSignedXml(data, key, SignType.MD5);
        }
    
        /**
         * 生成带有 sign 的 XML 格式字符串
         *
         * @param data Map类型数据
         * @param key API密钥
         * @param signType 签名类型
         * @return 含有sign字段的XML
         */
        public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
            String sign = generateSignature(data, key, signType);
            data.put(WXPayConstants.FIELD_SIGN, sign);
            return mapToXml(data);
        }
    
    
        /**
         * 判断签名是否正确
         *
         * @param xmlStr XML格式数据
         * @param key API密钥
         * @return 签名是否正确
         * @throws Exception
         */
        public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
            Map<String, String> data = xmlToMap(xmlStr);
            if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
                return false;
            }
            String sign = data.get(WXPayConstants.FIELD_SIGN);
            return generateSignature(data, key).equals(sign);
        }
    
        /**
         * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
         *
         * @param data Map类型数据
         * @param key API密钥
         * @return 签名是否正确
         * @throws Exception
         */
        public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
            return isSignatureValid(data, key, SignType.MD5);
        }
    
        /**
         * 判断签名是否正确,必须包含sign字段,否则返回false。
         *
         * @param data Map类型数据
         * @param key API密钥
         * @param signType 签名方式
         * @return 签名是否正确
         * @throws Exception
         */
        public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
            if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
                return false;
            }
            String sign = data.get(WXPayConstants.FIELD_SIGN);
            return generateSignature(data, key, signType).equals(sign);
        }
    
        /**
         * 生成签名
         *
         * @param data 待签名数据
         * @param key API密钥
         * @return 签名
         */
        public static String generateSignature(final Map<String, String> data, String key) throws Exception {
            return generateSignature(data, key, SignType.MD5);
        }
    
        /**
         * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
         *
         * @param data 待签名数据
         * @param key API密钥
         * @param signType 签名方式
         * @return 签名
         */
        public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
            Set<String> keySet = data.keySet();
            String[] keyArray = keySet.toArray(new String[keySet.size()]);
            Arrays.sort(keyArray);
            StringBuilder sb = new StringBuilder();
            for (String k : keyArray) {
                if (k.equals(WXPayConstants.FIELD_SIGN)) {
                    continue;
                }
                if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                    sb.append(k).append("=").append(data.get(k).trim()).append("&");
            }
            sb.append("key=").append(key);
            if (SignType.MD5.equals(signType)) {
                return MD5(sb.toString()).toUpperCase();
            }
            else if (SignType.HMACSHA256.equals(signType)) {
                return HMACSHA256(sb.toString(), key);
            }
            else {
                throw new Exception(String.format("Invalid sign_type: %s", signType));
            }
        }
    
    
        /**
         * 获取随机字符串 Nonce Str
         *
         * @return String 随机字符串
         */
        public static String generateNonceStr() {
            char[] nonceChars = new char[32];
            for (int index = 0; index < nonceChars.length; ++index) {
                nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
            }
            return new String(nonceChars);
        }
    
    
        /**
         * 生成 MD5
         *
         * @param data 待处理数据
         * @return MD5结果
         */
        public static String MD5(String data) throws Exception {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }
    
        /**
         * 生成 HMACSHA256
         * @param data 待处理数据
         * @param key 密钥
         * @return 加密结果
         * @throws Exception
         */
        public static String HMACSHA256(String data, String key) throws Exception {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }
    
        /**
         * 日志
         * @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();
        }
    
    
        /**
         * 从请求request中获取IP地址
         *
         * @param request
         * @return IP
         */
        public static String getIp(HttpServletRequest request) {
            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Real-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            if (ip != null && ip.toLowerCase().contains("x-forwarded-for")) {
                String ip_temp = "";
                if (ip.contains(":") && ip.split(":").length > 0 && !ip.endsWith(":")) {
                    ip_temp = ip.split(":")[1];
                    if (StringUtils.isBlank(ip_temp)) {
                        ip_temp = ip.substring(0, ip.lastIndexOf("X-Forwarded-For"));
                    }
                } else {
                    ip_temp = ip.substring(0, ip.lastIndexOf("X-Forwarded-For"));
                }
                ip = ip_temp.replaceAll(" ", "");
            }
            if (ip != null && ip.indexOf(",") > -1) {
                String ip_temp = ip.split(",")[0];
                ip_temp = ip_temp.replaceAll(" ", "");
                if (ip_temp.startsWith("10.") && ip.split(",").length > 1) {
                    ip = ip.split(",")[1];
                    ip = ip.replaceAll(" ", "");
                } else {
                    ip = ip_temp;
                }
            }
            return ip;
        }
    
    
    }
    
    
    package cn.com.artlife.archiveCenterService.model;
    
    /**
     * 通用接口返回对象
     */
    public class CommonResponse {
    
        //默认成功状态
        public static final String SUCCESS = "0";
        //默认失败状态
        public static final String ERROR = "-1";
    
        //接口状态
        private String status = ERROR;
        //接口消息
        private String msg = "";
        //接口返回对象
        private Object data = null;
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        //失败
        public void setFailed(String status,String msg){
            setStatus(status);
            setMsg(msg);
        }
    
        //带返回对象的成功
        public void setSuccess(Object data){
            setStatus(SUCCESS);
            setData(data);
        }
    
        //没有返回对象的成功
        public void setSuccess(){
            setSuccess(null);
        }
    
    //    //判断是否成功
    //    public boolean isSuccess(){
    //        return SUCCESS.equals(status);
    //    }
    }
    
    

    常见问题

    1. 网络环境未能通过安全验证,请稍后再试

    在这里插入图片描述

    商户侧统一下单传的终端IP(spbill_create_ip)与用户实际调起支付时微信侧检测到的终端IP不一致导致的,这个问题一般是商户在统一下单时没有传递正确的终端IP到spbill_create_ip导致

    1. 商家参数格式有误,请联系商家解决

    在这里插入图片描述

    当前调起H5支付的referer为空导致,一般是因为直接访问页面调起H5支付,js直接跳转就可以,不要复制链接再浏览器中打开,会报上面这个错。

    3.商家存在未配置的参数,请联系商家解决

    在这里插入图片描述

    当前调起H5支付的域名(微信侧从referer中获取)与申请H5支付时提交的授权域名不一致,这里是H5的支付域名,不是公众号支付

    1. 支付请求已失效,请重新发起支付

    在这里插入图片描述

    统一下单返回的MWEB_URL生成后,有效期为5分钟,如超时请重新生成MWEB_URL后再发起支付

    java代码

    github 地址

    展开全文
  • 微信支付与退款

    2020-08-05 19:09:22
    微信支付1.1 调用统一下单接口--生成预付单1.1.1 配置信息的封装1.1.2 PayService调用统一下单接口1.1.3 PayController1.1.4 测试生成预付单并返回支付参数1.2 统一下单接口---微信H5调起支付1.3 用户下单到支付...
  • 使用公众号支付申请退款需要验证商户API证书,登录微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全,下载证书。apiclient_cert.p12是商户证书文件,除PHP外的开发均使用此...
  • java实现微信h5支付

    万次阅读 热门讨论 2019-03-22 16:04:31
    微信h5支付需要在微信商户平台-》产品中心开通h5支付。 官网提供的开发文档中需要的参数: h5支付主要是scene_info中的参数wap_url必须是可以访问到的地址。spbill_create_ip的获取必须和调起微信支付的ip...
  • 之前没有做过微信支付,在CSDN找了很多案例都没有看明白, 下载后要么是不完整缺少文件,要么是各种原因...要求是传递一个金额值给用户H5页面,用户微信支付微信dll306181140,着急在线等,如果能解决问题还可以加分
  • 微信浏览器内部可以用jsapi接口调用微信支付,那么如果我在微信浏览器意外(比如360手机浏览器)的H5页面,想要调起微信支付(京东等大型商城有这种功能),那么我想问一下,这种功能需要另外找微信那边申请接口吗...
  • 这是微信h5支付方便的地方。 下面子恒老师详细说一下mweb url… 一、 生成微信h5支付mweb_url支付链接 想要获取mweb_url, 需要调用微信h5支付的统一下单接口 https://api.mch.weixin.qq.com/pay/unifiedor...
  • php 微信H5支付源码示例(超简单)

    千次阅读 2018-07-29 17:33:54
    超简单,超好用。。。。简单修改参数就行 转载:https://download.csdn.net/download/computer001/10371433
  • iOS 封装的H5 APP 调用微信H5支付,进入微信支付界面后,点击取消,或者支付完成返回不能返回到App,而是打开了手机系统的safari浏览器。 用的h5+ 请问有解决方案吗
  • 先将最终获取到的微信链接打印出来,发现只有 http://xxxxxxxxxxpackge=1234546http....(后面这段是redirect_url) 所以确定是最后的redirect_url参数只有值没有键。改为: http://xxxxxxxxxxpackge=1234546&...
  • 在iOS APP发起H5微信支付

    万次阅读 2018-01-11 14:43:46
    场景介绍 H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端...这个场景介绍来自微信H5支付的开发文档,说建议不要在APP端使用H5支付,但是
  • 在iOS的webview中,h5自己调用微信支付,能够成功起调,并且能完成支付,但是点击完成时,跳转到safari浏览器中,并不能跳回app。
  • 2.我在iis日志中发现,微信有给我服务器post 这个地址。只是代码没有调用,标识302 3.我在这个回调页中第一行代码就输出调试代码,没反应。证明调用没有进来过。 有没有人能提供个线索,还有就是怎么能获取所有...
  • 这里有一个iOS工程,整个工程几乎就是个webview来显示一个商城类网站,现在要在网站里加入微信支付功能 如果微信支付H5的方式做,在iOSapp里的webview中可以跳转到微信支付吗?安卓可以吗? 谢谢
1 2 3 4 5 ... 20
收藏数 11,266
精华内容 4,506
关键字:

微信h5支付