• 微信支付开发h5调用


    这两天做微信支付开发。碰到大坑。纠结死我了。好不容做完。

    后台java:直接上代码:注意区分前后端的变量大小写。。。

    @RequestMapping(value = "/index")
        public Model  index(@RequestParam(value = "openid", required = true) String openid ,Model model,HttpServletRequest request) throws Exception{
            logger.info("************openid***********为:"+openid);
            //获取prepayid
            Map<String ,String > map=new HashMap<String,String>();
            WeiXinConfig wcf=weiXinBaseService.getWeiXinConfig();
             String nonceStr=UUID.randomUUID().toString().substring(0, 32);
             
            oauthService.shareFactory(request);
            String appid=wcf.getAppid();
             long timestamp = System.currentTimeMillis() / 1000;
            map.put("appid", appid );
            map.put("mch_id", WebConfig.get("pay.mch_id"));
            map.put("nonce_str",nonceStr);
            map.put("body",  WebConfig.get("pay.body"));
            map.put("out_trade_no", payWxUtil.orderNum());
            map.put("total_fee", WebConfig.get("pay.price"));
            map.put("spbill_create_ip",request.getRemoteAddr());
            map.put("notify_url", WebConfig.get("hostAddress")+request.getContextPath()+"/babyShow/payInfo/info");
            map.put("trade_type", "JSAPI");
            map.put("openid", openid);
            String paySign=SignUtil.getPayCustomSign(map,WebConfig.get("pay.key"));
            map.put("sign",paySign);
            String xml=    CommonUtil.ArrayToXml(map);
            String prepayid=    payWxUtil.getPrepayid(xml);
            logger.info("prepareid*****************************="+prepayid);
           //封装h5页面调用参数
            Map<String ,String > signMap=new HashMap<String ,String >();
            signMap.put("appId", appid);
            logger.info("appId="+appid);
            signMap.put("timeStamp", timestamp+"");
            logger.info("timeStamp="+timestamp);
            signMap.put("package", "prepay_id="+prepayid);
            logger.info("package="+"prepay_id="+prepayid);
            signMap.put("signType", "MD5");
            logger.info("singType="+"MD5");
            signMap.put("nonceStr", nonceStr);
            logger.info("nonceStr="+nonceStr);
            model.addAttribute("paytimestamp", timestamp);
            model.addAttribute("paypackage", "prepay_id="+prepayid);
            model.addAttribute("paynonceStr", nonceStr);
            model.addAttribute("paysignType", "MD5");
            String paySign2=SignUtil.getPayCustomSign(signMap,WebConfig.get("pay.key"));
            model.addAttribute("paySign",paySign2 );
            logger.info("paySign="+paySign2);
            return model;
            
        }

    以上代码获取openid需要根据网页授权来获取。这里就不多讲了。主要讲讲获取prepayid和生成h5页面所需参数,

    这里面比较麻烦的就是签名的获取

    查看方法SignUtil.getPayCustomSign(signMap,WebConfig.get("pay.key"))

    代码如下

      /**
         * 获取支付所需签名
         * @param ticket
         * @param timeStamp
         * @param card_id
         * @param code
         * @return
         * @throws Exception
         */
        public static String getPayCustomSign(Map<String, String> bizObj,String key) throws Exception {
            
            String bizString = CommonUtil.FormatBizQueryParaMap(bizObj,
                    false);
            logger.info(bizString);
            return MD5SignUtil.sign(bizString, key);
        }
       

    其中CommonUtil.FormatBizQueryParaMap是用来做字典排序的。有参考了网上的例子。就没单独做。

    public static String FormatBizQueryParaMap(Map<String, String> paraMap,
                boolean urlencode) throws Exception {
    
            String buff = "";
            try {
                List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
                        paraMap.entrySet());
    
                Collections.sort(infoIds,
                        new Comparator<Map.Entry<String, String>>() {
                            public int compare(Map.Entry<String, String> o1,
                                    Map.Entry<String, String> o2) {
                                return (o1.getKey()).toString().compareTo(
                                        o2.getKey());
                            }
                        });
    
                for (int i = 0; i < infoIds.size(); i++) {
                    Map.Entry<String, String> item = infoIds.get(i);
                    //System.out.println(item.getKey());
                    if (item.getKey() != "") {
                        
                        String key = item.getKey();
                        String val = item.getValue();
                        if (urlencode) {
                            val = URLEncoder.encode(val, "utf-8");
    
                        }
                        buff += key + "=" + val + "&";
    
                    }
                }
    
                if (buff.isEmpty() == false) {
                    buff = buff.substring(0, buff.length() - 1);
                }
            } catch (Exception e) {
                throw new Exception(e.getMessage());
            }
            return buff;
        }
    
       

    其中MD5SignUtil.sign(bizString, key)方法如下

    public static String sign(String content, String key)
                throws Exception{
            String signStr = "";
            signStr = content + "&key=" + key;
    
            return MD5Util.MD5(signStr).toUpperCase();
    
        }
    其中MD5Util.MD5(signStr)方法如下
        public final static String MD5(String s) {
            char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};       
            try {
                byte[] btInput = s.getBytes();
                MessageDigest mdInst = MessageDigest.getInstance("MD5");
                mdInst.update(btInput);
                byte[] md = mdInst.digest();
                int j = md.length;
                char str[] = new char[j * 2];
                int k = 0;
                for (int i = 0; i < j; i++) {
                    byte byte0 = md[i];
                    str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                    str[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(str);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
           }
        }
    CommonUtil.ArrayToXml(map)方法如下

    public static String ArrayToXml(Map<String, String> arr) {
            String xml = "<xml>";
            
            Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
            while (iter.hasNext()) {
                Entry<String, String> entry = iter.next();
                String key = entry.getKey();
                String val = entry.getValue();
                if (IsNumeric(val)) {
                    xml += "<" + key + ">" + val + "</" + key + ">";
    
                } else
                    xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
            }
    
            xml += "</xml>";
            return xml;
        }
    public static boolean IsNumeric(String str) {
            if (str.matches("\d *")) {
                return true;
            } else {
                return false;
            }
        }
    
    
    
     

    发送请求到微信获取prepayid代码如下

    public static String URL="https://api.mch.weixin.qq.com/pay/unifiedorder";
        @SuppressWarnings("deprecation")
        public  JSONObject getPrepayJson(String xml){
            HttpClient httpClient = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) ); 
            InputStream is = null;
             PostMethod method=null;
            try {
                String url =URL;
                 method = HttpClientUtils.postMethod(url);
                 method.setRequestBody(xml);
                httpClient.executeMethod(method);
                //读取响应
                 is = method.getResponseBodyAsStream();
                JSONObject o =Xml2JsonUtil.xml2JSON(is);
                return o;
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                if(method!=null){
                    method.releaseConnection();
                }
                if(is!=null){
                    try {
                        is.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
            return null;
        }
        public  String getPrepayid(String xml){
            try {
                JSONObject jo=getPrepayJson(xml);
                JSONObject element=jo.getJSONObject("xml");
                String prepayid=((JSONArray)element.get("prepay_id")).get(0).toString();
                return prepayid;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        public  String orderNum(){
            String chars = "0123456789";
            String order = System.currentTimeMillis()+"";
            String res = "";
            for (int i = 0; i < 19; i++) {
                Random rd = new Random();
                res += chars.charAt(rd.nextInt(chars.length() - 1));
            }
            order+=res;
            return order;
        }

    xml转json方法Xml2JsonUtil.xml2JSON(is);。摘自网络

     /** 
             * 转换一个xml格式的字符串到json格式 
             *  
             * @param xml 
             *            xml格式的字符串 
             * @return 成功返回json 格式的字符串;失败反回null 
             */  
            @SuppressWarnings("unchecked")  
            public static  JSONObject xml2JSON(InputStream is) {  
                JSONObject obj = new JSONObject();  
                try {  
                    SAXReader sb = new SAXReader();  
                    Document doc = sb.read(is);
                    Element root = doc.getRootElement();  
                    obj.put(root.getName(), iterateElement(root));  
                    return obj;  
                } catch (Exception e) {  
                    log.error("传入XML后转换JSON出现错误===== Xml2JsonUtil-->xml2JSON============>>",e);  
                    return null;  
                }  
            }  
    
     /** 
             * 一个迭代方法 
             *  
             * @param element 
             *            : org.jdom.Element 
             * @return java.util.Map 实例 
             */  
            @SuppressWarnings("unchecked")  
            private static Map  iterateElement(Element element) {  
                List jiedian = element.elements() ;
                Element et = null;  
                Map obj = new HashMap();  
                List list = null;  
                for (int i = 0; i < jiedian.size(); i++) {  
                    list = new LinkedList();  
                    et = (Element) jiedian.get(i);  
                    if (et.getTextTrim().equals("")) {  
                        if (et.elements().size() == 0)  
                            continue;  
                        if (obj.containsKey(et.getName())) {  
                            list = (List) obj.get(et.getName());  
                        }  
                        list.add(iterateElement(et));  
                        obj.put(et.getName(), list);  
                    } else {  
                        if (obj.containsKey(et.getName())) {  
                            list = (List) obj.get(et.getName());  
                        }  
                        list.add(et.getTextTrim());  
                        obj.put(et.getName(), list);  
                    }  
                }  
                return obj;  
            }  
     

    服务端基本这么多

    前端被微信和我自己挖了大坑,整整搞了两天,擦

    先是看的微信文档jssdk文档(开始埋坑。。)

    页面引入js

    加入

        wx.config({
            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: '${appid}', // 必填,公众号的唯一标识
            timestamp: '${timestamp}', // 必填,生成签名的时间戳
            nonceStr: '${nonceStr}', // 必填,生成签名的随机串
            signature: '${signature}',// 必填,签名,见附录1
            jsApiList: [
                'chooseWXPay'
            ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
            
        });

    嗯。配置好了。如果传入jsconfig的参数。。。

    然后看到jssdk里面有发送一个微信支付请求?

    嗯加进去

    
    
    function pay(){
            wx.chooseWXPay({
                timestamp: ${paytimestamp}, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
                nonceStr: '${paynonceStr}', // 支付签名随机串,不长于 32 位
                package: '${paypackage}', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
                signType: '${paysignType}', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                paySign: '${paySign}', // 支付签名
                success: function (res) {
                    alert("支付成功");
                    // 支付成功后的回调函数
                }
            });
        }
    
    
    
     

    然后发布,执行调试。。尼玛。怎么样都支付不了。一会儿报订单信息错误、一会儿报签名错误。。。找了N多资料均不对

    后来想啊。大不了用商户平台提供的文档写接口什么的。于是加入了代码

          function onBridgeReady(){
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', {
                       "appId" : "${appid}",     //公众号名称,由商户传入     
                        "timeStamp":"${paytimestamp}",         //时间戳,自1970年以来的秒数     
                        "nonceStr" : "${paynonceStr}", //随机串     
                        "package" : "${paypackage}",     
                        "signType" : "${paysignType}",         //微信签名方式:     
                        "paySign" : "${paySign}" //微信签名 
                   },
                    function(res){
                       alert(res.err_msg);  // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返
     
                   }
               ); 
             }
        function pay2(){  
            if (typeof WeixinJSBridge == "undefined"){
               if( document.addEventListener ){
                     document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                 }else if (document.attachEvent){
                     document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
                    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                }
             }else{
               onBridgeReady();
             }
         }

    发布调试。。。还是不对。。两个按钮pay()和pay2()均不对。pay方法还是一会儿订单信息错误一会儿签名错误。pay2()方法则一直报签名错误。。。尼玛。吧经历放后台签名。。怎么改都不行。签名明明对的啊。可以获取prepayid的。为毛封装h5参数时就出错了呢。。。折腾啊。。。搞了N久。。就尼玛不对。。。后来一个小错误pay方法js报错了。。结果pay2方法确可以执行了。。。吧pay的js报错去了之后pay2又不能用了。。我擦。。哥有预感。。难道两种方式冲突了??于是我把config和pay方法删除了再试试。。。尼玛果然可以了。不能加入config和pay。。。果然可以了。。。我擦。折腾的。。微信啃爹啊。第一种方式无法用有没有,误导人啊。。。正确结果就是商品平台的文档使用方法js如下:

          function onBridgeReady(){
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', {
                       "appId" : "${appid}",     //公众号名称,由商户传入     
                        "timeStamp":"${paytimestamp}",         //时间戳,自1970年以来的秒数     
                        "nonceStr" : "${paynonceStr}", //随机串     
                        "package" : "${paypackage}",     
                        "signType" : "${paysignType}",         //微信签名方式:     
                        "paySign" : "${paySign}" //微信签名 
                   },
                    function(res){
                       alert(res.err_msg);  // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返
     
                   }
               ); 
             }
        function pay(){  
            if (typeof WeixinJSBridge == "undefined"){
               if( document.addEventListener ){
                     document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                 }else if (document.attachEvent){
                     document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
                    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                }
             }else{
               onBridgeReady();
             }
         }

    至此。终于把微信支付搞定了。留着博客。防止后人走弯路(config里面不能加入chooseWXPay方法。。切记)

  • 相关阅读:
    技术周刊 · 耿耿星河欲曙天 | SpaceX 上的前端架构;跨平台新选择;开源世界的新“大门”;用户推荐算法的敲门砖……
    说说SVG的feTurbulence滤镜
    机器学习进阶
    小程序与动画的故事
    技术周刊 · 迢迢山径峻 | Web 开发成长图谱;下一代前端构建技术;AI 应用下支离破碎的真实;不懂产品不会开发;虚拟货币是新时代的黄金
    从中断机制看 React Fiber 技术
    三分钟了解数字人民币
    凹凸技术揭秘:如何服务 toG 项目——数字人民币项目前端总结
    使用 Phaser3+Matter.js 实现“合成大西瓜”游戏
    痞子衡嵌入式:快速定位i.MXRT600板级设计ISP[2:0]启动模式引脚上电时序问题的方法
  • 原文地址:https://www.cnblogs.com/wanglonghai/p/4569354.html
Copyright © 2020-2023  润新知